#ifndef GTL_HASH_HEADER_GUARD
#define GTL_HASH_HEADER_GUARD

/*
TODO:
- GUID
- MD5, FastHash
- CRC32 (jest deklaracja w gtlStrings.h)
*/

#include "external/hash.h"
#include "gtlCompilerSpecific.h"

namespace GTL
{
  namespace Hash
  {
    ///************************************************************************/
    ///* Checksums                                                            */
    ///************************************************************************/

		/// Calculates the CRC32 of the buffer. If size == 0 expect incorrect behavior
    uint32_t CRC32(const uint8_t* buffer, size_t size);
    
    ///************************************************************************/
    ///* Hash functions                                                       */
    ///************************************************************************/
  
    /// http://www.concentric.net/~Ttwang/tech/inthash.htm
    inline uint32_t IntegerHashWithKey(uint64_t hash_key, uint32_t c) {
      uint32_t a = static_cast<uint32_t>(hash_key)       & 0xFFFFFFFF;
      uint32_t b = static_cast<uint32_t>(hash_key >> 32) & 0xFFFFFFFF;

      a -= (b + c);  a ^= (c >> 13);
      b -= (c + a);  b ^= (a << 8);
      c -= (a + b);  c ^= (b >> 13);
      a -= (b + c);  a ^= (c >> 12);
      b -= (c + a);  b ^= (a << 16);
      c -= (a + b);  c ^= (b >> 5);
      a -= (b + c);  a ^= (c >> 3);
      b -= (c + a);  b ^= (a << 10);
      c -= (a + b);  c ^= (b >> 15);

      return c;
    }

		///************************************************************************/
		///* Compile-time string hash                                             */
		///************************************************************************/

		/// Only to be used with string literals

		template <unsigned int N, unsigned int I>
		struct CompileTimeStringHash
		{
			inline static unsigned int Hash(const char (&str)[N])
			{
				return (CompileTimeStringHash<N, I-1>::Hash(str) ^ str[I-1])*16777619u;
			}
		};

		template <unsigned int N>
		struct CompileTimeStringHash<N, 1>
		{
			inline static unsigned int Hash(const char (&str)[N])
			{
				return (2166136261u ^ str[0])*16777619u;
			}
		};

		inline unsigned int CompileTimeStringHash_IterativeRuntimeEquivalent(const char* str)
		{
			unsigned int hash = (2166136261u ^ str[0])*16777619u;
			while (*str++)
			{
				hash = (hash ^ (*str))*16777619u;
			}
			return hash;
		}

		template <unsigned int N>
		inline unsigned int StringHash(const char (&str)[N])
		{
#ifdef GTL_DEBUG
			return CompileTimeStringHash_IterativeRuntimeEquivalent(str);
#else
			return CompileTimeStringHash<N, N>::Hash(str);
#endif
		}

    ///************************************************************************/
    ///* Hash structures                                                      */
    ///************************************************************************/
    
    /// TODO: Ideal Hash Trie
    /// Moze przeniesc do gtlStructures?
  }
}

#endif